#include "lua.hpp"
#include "Pinea.h"
#include <iostream>
#include <string>

int l_pineaInit(lua_State *L) {
    const char *title = luaL_checkstring(L, 1);
    int width = luaL_checkinteger(L, 2);
    int height = luaL_checkinteger(L, 3);
    pineaInit(title, width, height);
    return 0;
}

int l_pineaQuit(lua_State *L) {
    pineaQuit();
    return 0;
}

int l_pineaShow(lua_State *L) {
    pineaShow();
    return 0;
}

void setTableInt(lua_State *L, const char *key, int value) {
    lua_pushinteger(L, value);
    lua_setfield(L, -2, key);
}

void setTableDouble(lua_State *L, const char *key, double value) {
    lua_pushnumber(L, value);
    lua_setfield(L, -2, key);
}

void setTableFloat(lua_State *L, const char *key, float value) {
    lua_pushnumber(L, value);
    lua_setfield(L, -2, key);
}

void getTableInt(lua_State *L, int index, const char *key, int &value) {
    lua_getfield(L, index, key);
    value = luaL_checkinteger(L, -1);
    lua_pop(L, 1);
}

void getTableUint8(lua_State *L, int index, const char *key, uint8_t &value) {
    lua_getfield(L, index, key);
    value = (uint8_t)luaL_checkinteger(L, -1);
    lua_pop(L, 1);
}

void getTableDouble(lua_State *L, int index, const char *key, double &value) {
    lua_getfield(L, index, key);
    value = luaL_checknumber(L, -1);
    lua_pop(L, 1);
}

void getTableFloat(lua_State *L, int index, const char *key, float &value) {
    lua_getfield(L, index, key);
    value = (float)luaL_checknumber(L, -1);
    lua_pop(L, 1);
}

int l_pineaPollEvent(lua_State *L) {
    PineaEvent event{};
    bool result = pineaPollEvent(&event);
    if (!result) {
        lua_pushboolean(L, false);
        return 1;
    }
    lua_pushboolean(L, true);
    // return table
    lua_newtable(L);
    setTableInt(L, "type", event.type);
    setTableInt(L, "key", event.key);
    setTableInt(L, "x", event.x);
    setTableInt(L, "y", event.y);
    return 2;
}

int l_pineaClear(lua_State *L) {
    luaL_checktype(L, 1, LUA_TTABLE);
    PineaColor color{};
    getTableUint8(L, 1, "r", color.r);
    getTableUint8(L, 1, "g", color.g);
    getTableUint8(L, 1, "b", color.b);
    getTableUint8(L, 1, "a", color.a);
    pineaClear(&color);
    return 0;
}

int l_pineaRender(lua_State *L) {
    pineaRender();
    return 0;
}

int l_pineaFillRect(lua_State *L) {
    luaL_checktype(L, 1, LUA_TTABLE);
    luaL_checktype(L, 2, LUA_TTABLE);
    PineaRect rect{};
    getTableFloat(L, 1, "x", rect.x);
    getTableFloat(L, 1, "y", rect.y);
    getTableFloat(L, 1, "w", rect.w);
    getTableFloat(L, 1, "h", rect.h);
    PineaColor color{};
    getTableUint8(L, 2, "r", color.r);
    getTableUint8(L, 2, "g", color.g);
    getTableUint8(L, 2, "b", color.b);
    getTableUint8(L, 2, "a", color.a);
    pineaFillRect(&rect, &color);
    return 0;
}

int l_pineaLoadImage(lua_State *L) {
    const char *path = luaL_checkstring(L, 1);
    PineaImage *image = pineaLoadImage(path);
    lua_pushlightuserdata(L, image);
    return 1;
}

int l_pineaDrawImage(lua_State *L) {
    PineaImage *image = (PineaImage *)lua_touserdata(L, 1);
    PineaRect *stcPtr = nullptr;
    if (lua_istable(L, 2)) {
        PineaRect src{};
        stcPtr = &src;
        getTableFloat(L, 2, "x", stcPtr->x);
        getTableFloat(L, 2, "y", stcPtr->y);
        getTableFloat(L, 2, "w", stcPtr->w);
        getTableFloat(L, 2, "h", stcPtr->h);
    }
    PineaRect *dstPtr = nullptr;
    if (lua_istable(L, 3)) {
        PineaRect dst{};
        dstPtr = &dst;
        getTableFloat(L, 3, "x", dstPtr->x);
        getTableFloat(L, 3, "y", dstPtr->y);
        getTableFloat(L, 3, "w", dstPtr->w);
        getTableFloat(L, 3, "h", dstPtr->h);
    }
    double angle = luaL_checknumber(L, 4);
    int flip = luaL_checkinteger(L, 5);
    pineaDrawImage(image, stcPtr, dstPtr, angle, flip);
    return 0;
}

int l_pineaDropImage(lua_State *L) {
    PineaImage *image = (PineaImage *)lua_touserdata(L, 1);
    pineaDropImage(image);
    return 0;
}

int l_pineaLoadMusic(lua_State *L) {
    const char *path = luaL_checkstring(L, 1);
    PineaAudio *audio = pineaLoadMusic(path);
    lua_pushlightuserdata(L, audio);
    return 1;
}

int l_pineaPlayMusic(lua_State *L) {
    PineaAudio *audio = (PineaAudio *)lua_touserdata(L, 1);
    bool loop = lua_toboolean(L, 2);
    pineaPlayMusic(audio, loop);
    return 0;
}

int l_pineaPauseMusic(lua_State *L) {
    pineaPauseMusic();
    return 0;
}

int l_pineaResumeMusic(lua_State *L) {
    pineaResumeMusic();
    return 0;
}

int l_pineaStopMusic(lua_State *L) {
    pineaStopMusic();
    return 0;
}

int l_pineaDropMusic(lua_State *L) {
    PineaAudio *audio = (PineaAudio *)lua_touserdata(L, 1);
    pineaDropMusic(audio);
    return 0;
}

int l_pineaLoadSound(lua_State *L) {
    const char *path = luaL_checkstring(L, 1);
    PineaAudio *audio = pineaLoadSound(path);
    lua_pushlightuserdata(L, audio);
    return 1;
}

int l_pineaPlaySound(lua_State *L) {
    PineaAudio *audio = (PineaAudio *)lua_touserdata(L, 1);
    int loops = luaL_checkinteger(L, 2);
    pineaPlaySound(audio, loops);
    return 0;
}

int l_pineaDropSound(lua_State *L) {
    PineaAudio *audio = (PineaAudio *)lua_touserdata(L, 1);
    pineaDropSound(audio);
    return 0;
}

int l_pineaGetTime(lua_State *L) {
    lua_pushinteger(L, pineaGetTime());
    return 1;
}

void RegisterPineaFunctions(lua_State *L) {
    lua_register(L, "l_pineaInit", l_pineaInit);
    lua_register(L, "l_pineaQuit", l_pineaQuit);
    lua_register(L, "l_pineaShow", l_pineaShow);
    lua_register(L, "l_pineaPollEvent", l_pineaPollEvent);
    lua_register(L, "l_pineaClear", l_pineaClear);
    lua_register(L, "l_pineaRender", l_pineaRender);
    lua_register(L, "l_pineaFillRect", l_pineaFillRect);
    lua_register(L, "l_pineaLoadImage", l_pineaLoadImage);
    lua_register(L, "l_pineaDrawImage", l_pineaDrawImage);
    lua_register(L, "l_pineaDropImage", l_pineaDropImage);
    lua_register(L, "l_pineaLoadMusic", l_pineaLoadMusic);
    lua_register(L, "l_pineaPlayMusic", l_pineaPlayMusic);
    lua_register(L, "l_pineaPauseMusic", l_pineaPauseMusic);
    lua_register(L, "l_pineaResumeMusic", l_pineaResumeMusic);
    lua_register(L, "l_pineaStopMusic", l_pineaStopMusic);
    lua_register(L, "l_pineaDropMusic", l_pineaDropMusic);
    lua_register(L, "l_pineaLoadSound", l_pineaLoadSound);
    lua_register(L, "l_pineaPlaySound", l_pineaPlaySound);
    lua_register(L, "l_pineaDropSound", l_pineaDropSound);
    lua_register(L, "l_pineaGetTime", l_pineaGetTime);
}

int main(int argc, char **argv) {
    std::string filename = "Main.lua";
    if (argc > 1) {
        filename = argv[1];
    }

    // Create a new Lua state
    lua_State *L = luaL_newstate();
    if (!L) {
        std::cout << "Error: Failed to create Lua state" << std::endl;
        return 1;
    }

    // Register Pinea functions
    RegisterPineaFunctions(L);

    // Open the Lua standard libraries
    luaL_openlibs(L);

    // Load and run the script
    if (luaL_loadfile(L, filename.c_str()) || lua_pcall(L, 0, 0, 0) != LUA_OK) {
        std::cout << "Error: " << lua_tostring(L, -1) << std::endl;
        lua_close(L);
        return 1;
    }

    lua_close(L);
    return 0;
}